/***
 * ОСТОРОЖНО! ТУТ МЯСО!!!
 *
 * код рабочий, но читать и темболее понимать что он делает крайнесложно. Пока есть дела и повожнее
 */

//TODO почистить и привисти в порядок код, добавить описание методов и классов
#ifndef __LOG_H__
#define __LOG_H__
#include <irrlicht.h>
#include <stdio.h>
#include <cstdarg>
#include <cstdio>
#include <thread/IThread.h>
//#define LOG(text) irr::log::Log::add(text,irr::ELL_WARNING,__FILE__,__FUNCTION__,__LINE__)
//#define LOGEX(text,type) irr::log::Log::add(text,type,__FILE__,__FUNCTION__,__LINE__)

#define QUOTE_(WHAT) #WHAT
#define QUOTE(WHAT) QUOTE_(WHAT)
//#define dbg(format, ...) irr::log::Log::format("%s: "format, __FILE__":"QUOTE(__LINE__), ## __VA_ARGS__)
#define dbg(format, ...) irr::log::Log::format(format,## __VA_ARGS__)

#define LOGEX(type,format,...) irr::log::Log::myprint(irr::thread::getCurrentID(),__FILE__,__FUNCTION__,__LINE__,type,format,## __VA_ARGS__)
#define LOG(format,...) irr::log::Log::myprint(irr::thread::getCurrentID(),__FILE__,__FUNCTION__,__LINE__,irr::ELL_INFORMATION,format,## __VA_ARGS__)
#define LOGE(format,...) irr::log::Log::myprint(irr::thread::getCurrentID(),__FILE__,__FUNCTION__,__LINE__,irr::ELL_ERROR,format,## __VA_ARGS__)
#define LOGW(format,...) irr::log::Log::myprint(irr::thread::getCurrentID(),__FILE__,__FUNCTION__,__LINE__,irr::ELL_WARNING,format,## __VA_ARGS__)
#define MARKER LOG("MARKER (thread %u): %s:%d",irr::thread::getCurrentID(),__FILE__,__LINE__)

namespace irr{
namespace log{

typedef void (*LogFunction)(irr::thread::ThreadID current_thread,char*text,irr::ELOG_LEVEL,const char*filename,const char*function,irr::u32 line,void*self);

class Log{
public:
	static void add(irr::thread::ThreadID current_chread,char*text,irr::ELOG_LEVEL level=irr::ELL_WARNING,const char*filename=0,const char*function=0,irr::u32 line=0);
	static void add(irr::thread::ThreadID current_chread,irr::core::stringc text,irr::ELOG_LEVEL level=irr::ELL_WARNING,const char*filename=0,const char*function=0,irr::u32 line=0);

	/**
	 * Устанавливает функцию, которая будет вызываться в случае попытки вывода логов
	 *
	 * @param function фукцния которая будет вызываться
	 * @param sender объект, который нужно передавать
	 */
	static void setReceiver(LogFunction function,void*sender=0);

	/**
	 * Перевод строку в тип лог-записи
	 *
	 * @param str строка
	 * @return формат лог-записи
	 */
	static irr::ELOG_LEVEL stringToLogLevel(irr::core::stringc str);

	static void myprint(irr::thread::ThreadID current_thread,const char*file,const char*function,int line,irr::ELOG_LEVEL level,const char*fmt,...){
		//return;
		   irr::core::stringc retStr("");

		   if (0 != fmt)
		   {
		      va_list marker;

		      // initialize variable arguments
		      va_start(marker, fmt);
#ifdef _IRR_WINDOWS_
		      size_t len = _vscprintf(fmt, marker) + 1;
#endif
#ifdef _IRR_COMPILE_WITH_X11_DEVICE_
		      char c;
		      //return vsnprintf(&c, 1, format, ap);
		      size_t len=vsnprintf(&c, 1, fmt, marker)+1;
		     // size_t len = vsprintf(&c,fmt, marker) + 1;
#endif



		      // Create a char vector to hold the formatted string.
		      irr::core::array<irr::c8> buffer(len);
		      int nWritten = vsprintf(&buffer[0],fmt,marker);
		      buffer[len-1]=char(0);
		      if (nWritten > 0)
		      {
		         retStr = &buffer[0];
		      }

		      // Reset variable arguments
		      va_end(marker);
		   }
		   add(current_thread,retStr,level,file,function,line);
	}


static irr::core::stringc format(const char *fmt, ...){
   //using std::string;
   //using irr::core::array;

   irr::core::stringc retStr("");

   if (NULL != fmt)
   {
      va_list marker = 0;

      // initialize variable arguments
      va_start(marker, fmt);

      // Get formatted string length adding one for NULL
#ifdef _IRR_WINDOWS_
      size_t len = _vscprintf(fmt, marker) + 1;
#endif
#ifdef _IRR_COMPILE_WITH_X11_DEVICE_
      size_t len = vsprintf(0,fmt, marker) + 1;
#endif


      // Create a char vector to hold the formatted string.
      irr::core::array<irr::c8> buffer(len);
      int nWritten = sprintf(&buffer[0],fmt,marker);

      if (nWritten > 0)
      {
         retStr = &buffer[0];
      }

      // Reset variable arguments
      va_end(marker);
   }

   return retStr;
}
};

#ifdef _IRR_WINDOWS_
//void sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...) {
//    sprintf(buffer, format, ...); //ERROR: expected primary-expression before '...' token
//}
#endif



}//namespace log
}//namespace irr
#endif
